/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#include "shapedata.h"
#include "mesh.h"

#ifdef _DEBUG
#ifdef _MSC_VER
   #define new new( _CLIENT_BLOCK, __FILE__, __LINE__)
#endif
#endif // _DEBUG

namespace Engine
{
	/***************************************************************************************************************/
	MeshShapeData::MeshDataMap MeshShapeData::m_datas;
	/***************************************************************************************************************/
	MeshDataPtr MeshShapeData::GetMeshData(const std::string& name)
	{
		MeshDataMap::iterator i  = m_datas.find(name);
		if(i != m_datas.end())
		{
			return i->second;
		}
		return MeshDataPtr();
	}
	/***************************************************************************************************************/
	void MeshShapeData::Save(const std::string& name, MeshDataPtr d)
	{
		if(m_datas.find(name) == m_datas.end())
		{
			m_datas[name] = d;
		}
	}
	/***************************************************************************************************************/
	void MeshShapeData::Delete(const std::string& name)
	{
		MeshDataMap::iterator i  = m_datas.find(name);
		if(i != m_datas.end())
		{
			if(!i->second.IsReferenced())
				i->second.Delete();
		}
	}
	/***************************************************************************************************************/
	MeshShapeData::~MeshShapeData()
	{
		Delete(entity->GetMesh()->GetName());
	}
	/***************************************************************************************************************/
	void MeshShapeData::Build()
	{
		if(m_builded)
			return;
		//create vertices and indices from entity
		MeshPtr mesh = entity->GetMesh();
		MeshDataPtr md = GetMeshData(mesh->GetName());
		if(!md.IsNull())
		{
			data = md;
		}
		else
		{
			data = RefPtr<MeshData>(new MeshData);
			VertexDeclarationPtr vdecl = mesh->GetVertexDeclaration();
			const VertexElement* velem = vdecl->FindElementBySemantic(VES_POSITION);
			if( 0 != velem && velem->GetType() == VET_FLOAT3)
			{
				for(unsigned i = 0; i < mesh->GetNumSubMeshes(); ++i)
				{
					SubMeshPtr smesh = mesh->GetSubMesh(i);
					HardWareBufferPtr indexData = smesh->GetIndexData();
					unsigned short* indexPtr = static_cast<unsigned short*>(indexData->Lock());
					data->indices.assign(&indexPtr[0],&indexPtr[smesh->GetFaceCount() * 3]);
					indexData->UnLock();
				}

				HardWareBufferPtr vertexData = mesh->GetVertexData();
				unsigned char* vertexPtr = static_cast<unsigned char*>(vertexData->Lock());

				for(unsigned i = 0; i < mesh->GetVertexCount(); ++i)
				{
					if(std::find(data->indices.begin(),data->indices.end(),i) != data->indices.end())
					{
						float pos[3]= {0};
						memcpy(pos,vertexPtr + (i * vdecl->GetVertexSize(0) + velem->GetOffset()),sizeof(float)*3);
						Vector3<float> v(pos[0],pos[1],pos[2]);

						std::vector<Vector3<float> >::iterator index = std::find(data->vertices.begin(), data->vertices.end(),v);
						if(index == data->vertices.end())
						{
							data->vertices.push_back(v);
							std::replace(data->indices.begin(),data->indices.end(),i,static_cast<unsigned int>(data->vertices.size()-1));
						}
						else
						{
							std::replace(data->indices.begin(),data->indices.end(),i,static_cast<unsigned int>(std::distance(data->vertices.begin(),index)));
						}
					}
				}
				vertexData->UnLock();

				Save(mesh->GetName(),data);
				m_builded = true;
			}
		}
	}
	/***************************************************************************************************************/
	ShapeData* BoxShapeData::Clone()const
	{
		return new BoxShapeData(*this);
	}
	/***************************************************************************************************************/
	ShapeData* SphereShapeData::Clone()const
	{
		return new SphereShapeData(*this);
	}
	/***************************************************************************************************************/
	ShapeData* MeshShapeData::Clone()const
	{
		return new MeshShapeData(*this);
	}
	/***************************************************************************************************************/
}
